package fr.ece.ing4.si.core;

import java.lang.Math;
import java.text.DecimalFormat;
import java.util.Random;
import java.util.concurrent.ExecutionException;

import javax.swing.SwingWorker;

public abstract class AbstractMonteCarlo extends SwingWorker {

    public void timeAndCompute(MonteCarloArgs MCArgs) throws InterruptedException, ExecutionException {
        long start = System.nanoTime();
        double result = computeOptionPrice(MCArgs);
        long end = System.nanoTime();

        String value = new DecimalFormat("$##,##0.00").format(result);
        System.out.println("The estimated value is " + value);
        System.out.println("Time (seconds) taken " + (end - start) / 1.0e9);
    }

    public double MonteCarloStandardOption(
            EOptionFlag flag, //specifies whether the option is a call or put option
            double S, //the current price of the option's underlying asset
            double X, //the strike price of the option
            double T, //time to maturity of the option (in years)
            double r, //the discount rate (risk-free interest rate)
            double b, //the cost of carry rate (cost of interest, dividends and any other additional costs)
            double v, //the volatility of the underlying asset (the standard deviation of its market price)
            int nSteps, //the number of steps to execute for each option price path (the number of intermediate points that will be calculated on each price path. The greater this value, the more accurate the estimation of the path's premium)
            int nSimulations //the number of simulations to run (number of price paths to explore)
            ) {

        double dt, st;
        double sum, drift, vSqrdt;
        int i, j, z;
        Random random = new Random();

        dt = T / nSteps;
        drift = (b - (v * v) / 2) * dt;
        vSqrdt = v * Math.sqrt(dt);
        //a double is initialized to 0 by default in VB
        //java doesn't allow non-intialized variable
        sum = 0;
        z = 0;

        if (flag == EOptionFlag.call) {
            z = 1;
        } else if (flag == EOptionFlag.put) {
            z = -1;
        }

        for (i = 1; i <= nSimulations; i++) {
            st = S;
            for (j = 1; j <= nSteps; j++) {
                st = st * Math.exp(drift + vSqrdt * random.nextGaussian());
            }
            sum = sum + Math.max(z * (st - X), 0);
        }

        return Math.exp(-r * T) * (sum / nSimulations);
    }
    
    public double MonteCarloStandardOptionOneSimulation(
            EOptionFlag flag, //specifies whether the option is a call or put option
            double S, //the current price of the option's underlying asset
            double X, //the strike price of the option
            double T, //time to maturity of the option (in years)
            double r, //the discount rate (risk-free interest rate)
            double b, //the cost of carry rate (cost of interest, dividends and any other additional costs)
            double v, //the volatility of the underlying asset (the standard deviation of its market price)
            int nSteps //the number of steps to execute for each option price path (the number of intermediate points that will be calculated on each price path. The greater this value, the more accurate the estimation of the path's premium)
            ) {
        return MonteCarloStandardOption(flag, S, X, T, r, b, v, nSteps, 1);
    }

    public abstract double computeOptionPrice(MonteCarloArgs MCArgs) throws InterruptedException, ExecutionException;
    @Override
    public abstract Object doInBackground();
}